/************************************
*  Copyright (C) 2010 Julien MOTTIN.
*  Contact: julien.mottin@gueydan.eu
*
*  This file is part of Gueydan.
*
*  Gueydan is free software: you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  Gueydan is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with Gueydan.  If not, see <http://www.gnu.org/licenses/>.
*
*************************************/
// Local
#include "style.h"
#include "util.h"
#include "xmldico.h"
#include "beerxmlhandler.h"

// QtCore
#include <QtGlobal>

// QtXml
#include <QDomNode>
#include <QDomElement>
#include <QDomDocument>
#include <QDomNodeList>
#include <QDomText>

Style::Style(const QString &iName,
             int iVersion,
             const QString &iCategory,
             int iCategoryNumber,
             const QString &iStyleLetter,
             const QString &iStyleGuide,
             const StyleType &iType,
             float iOGMin, float iOGMax,
             float iFGMin, float iFGMax,
             float iIBUMin, float iIBUMax,
             const Color &iColorMin, const Color &iColorMax)
:BeerXMLObject(iName,iVersion)
,_Category(iCategory)
,_CategoryNum(iCategoryNumber)
,_StyleLetter(iStyleLetter)
,_StyleGuide(iStyleGuide)
,_Style(iType)
,_OGMin(iOGMin)
,_OGMax(iOGMax)
,_FGMin(iFGMin)
,_FGMax(iFGMax)
,_IBUMin(iIBUMin)
,_IBUMax(iIBUMax)
,_ColorMin(iColorMin)
,_ColorMax(iColorMax)
,_ABVMin(1000)
,_ABVMax(-1000)
,_Notes()
,_Examples()
{
}

Style::Style()
:BeerXMLObject()
,_Category("")
,_CategoryNum(1)
,_StyleLetter("A")
,_StyleGuide("BJCP")
,_Style(STAle)
,_OGMin(1000)
,_OGMax(-1000)
,_FGMin(1000)
,_FGMax(-1000)
,_IBUMin(1000)
,_IBUMax(-1000)
,_ColorMin(1000)
,_ColorMax(-1000)
,_ABVMin(1000)
,_ABVMax(-1000)
,_Notes()
,_Examples()
{
}

Style::~Style()
{
}

bool Style::operator==(const Style &iOther) const
{
    return ((*this).BeerXMLObject::operator==(iOther) &&
            _Category == iOther._Category &&
            _CategoryNum == iOther._CategoryNum &&
            _StyleLetter == iOther._StyleLetter &&
            _StyleGuide == iOther._StyleGuide &&
            _Style == iOther._Style &&
            ComparePrecision(_OGMin,iOther._OGMin,0.01) &&
            ComparePrecision(_OGMax,iOther._OGMax,0.01) &&
            ComparePrecision(_FGMin,iOther._FGMin,0.01) &&
            ComparePrecision(_FGMax,iOther._FGMax,0.01) &&
            ComparePrecision(_IBUMin,iOther._IBUMin,0.01) &&
            ComparePrecision(_IBUMax,iOther._IBUMax,0.01) &&
            _ColorMin == iOther._ColorMin &&
            _ColorMax == iOther._ColorMax &&
            ComparePrecision(_ABVMin,iOther._ABVMin,0.01) &&
            ComparePrecision(_ABVMax,iOther._ABVMax,0.01) &&
            _Notes == iOther._Notes &&
            _Examples == iOther._Examples);
}

bool Style::operator==(const BeerXMLObject &iOther) const
{
    bool res = false;
    BeerXMLHandler hObj(&iOther);
    Style *pStyle = (Style*)hObj;
    if (pStyle)
    {
        res = (*this)==(*pStyle);
    }
    return res;
}

bool Style::operator!=(const Style &iOther) const
{
    return (!((*this)==iOther));
}

bool Style::operator!=(const BeerXMLObject &iOther) const
{
    return (!((*this)==iOther));
}

int Style::CreateFromXMLNode(const QDomNode &iNode)
{
    int oRC = 1;
    if (!iNode.isNull())
    {
        QDomElement StyleElement;

        // Case Document : check if doc has only one Hop element
        if (iNode.isDocument())
        {
            QDomDocument Doc = iNode.toDocument();
            QDomNodeList List = Doc.elementsByTagName(STYLE);
            if (List.length()==1)
                StyleElement = List.item(0).toElement();
        }
        // Default : cast to element
        else
            StyleElement = iNode.toElement();

        // Check element consistency
        if (!StyleElement.isNull() && STYLE==StyleElement.tagName())
        {
            bool Name = false;
            bool Version = false;
            bool Category = false;
            bool CategoryNum = false;
            bool StyleLetter = false;
            bool StyleGuide = false;
            bool Type = false;
            bool OGMin = false, OGMax = false;
            bool FGMin = false, FGMax = false;
            bool IBUMin = false, IBUMax = false;
            bool ColorMin = false, ColorMax = false;
            QDomNode node = StyleElement.firstChild();
            while (!node.isNull())
            {
                if (node.isElement())
                {
                    QDomElement elem = node.toElement();
                    QString name = elem.tagName();
                    QString text = elem.text();
                    if (name==STYLE_NAME)
                    {
                        _Name = text;
                        Name = true;
                    }
                    else if (name==STYLE_VERSION)
                    {
                        bool ok = false;
                        int v = text.toInt(&ok);
                        if (ok)
                        {
                            _Version = v;
                            Version = true;
                        }
                    }
                    else if (name==STYLE_CATEGORY)
                    {
                        _Category = text;
                        Category = true;
                    }
                    else if (name==STYLE_CATNUM)
                    {
                        bool ok = false;
                        int v = text.toInt(&ok);
                        if (ok)
                        {
                            _CategoryNum = v;
                            CategoryNum = true;
                        }
                    }
                    else if (name==STYLE_LETTER)
                    {
                        _StyleLetter = text;
                        StyleLetter = true;
                    }
                    else if (name==STYLE_GUIDE)
                    {
                        _StyleGuide = text;
                        StyleGuide = true;
                    }
                    else if (name==STYLE_TYPE)
                    {
                        const QString type = text.toLower();
                        Type = true;
                        if (type==STYLE_TYPE_LAGER.toLower())
                            _Style = STLager;
                        else if (type==STYLE_TYPE_ALE.toLower())
                            _Style = STAle;
                        else if (type==STYLE_TYPE_WHEAT.toLower())
                            _Style = STWheat;
                        else if (type==STYLE_TYPE_MEAD.toLower())
                            _Style = STMead;
                        else if (type==STYLE_TYPE_MIXED.toLower())
                            _Style = STMixed;
                        else if (type==STYLE_TYPE_CIDER.toLower())
                            _Style = STCider;
                        else
                            Type = false;
                    }
                    else if (name==STYLE_OGMIN)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                        {
                            _OGMin = v;
                            OGMin = true;
                        }
                    }
                    else if (name==STYLE_OGMAX)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                        {
                            _OGMax = v;
                            OGMax = true;
                        }
                    }
                    else if (name==STYLE_FGMIN)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                        {
                            _FGMin = v;
                            FGMin = true;
                        }
                    }
                    else if (name==STYLE_FGMAX)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                        {
                            _FGMax = v;
                            FGMax = true;
                        }
                    }
                    else if (name==STYLE_IBUMIN)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                        {
                            _IBUMin = v;
                            IBUMin = true;
                        }
                    }
                    else if (name==STYLE_IBUMAX)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                        {
                            _IBUMax = v;
                            IBUMax = true;
                        }
                    }
                    else if (name==STYLE_COLORMIN)
                    {
                        bool ok = false;
                        _ColorMin.FromString(text,&ok);
                        if (ok)
                            ColorMin = true;
                    }
                    else if (name==STYLE_COLORMAX)
                    {
                        bool ok = false;
                        _ColorMax.FromString(text,&ok);
                        if (ok)
                            ColorMax = true;
                    }
                    else if (name==STYLE_ABVMIN)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                            _ABVMin = v;
                    }
                    else if (name==STYLE_ABVMAX)
                    {
                        bool ok = false;
                        float v = text.toFloat(&ok);
                        if (ok)
                            _ABVMax = v;
                    }
                    else if (name==STYLE_NOTES)
                    {
                        _Notes = text;
                    }
                    else if (name==STYLE_EXAMPLES)
                    {
                        _Examples = text;
                    }
                }

                // Iterate
                node = node.nextSibling();
            }

            // Check & update output
            if (Name && Version && Category && CategoryNum &&
                StyleLetter && StyleGuide && Type &&
                OGMin && OGMax && FGMin && FGMax &&
                IBUMin && IBUMax && ColorMin && ColorMax)
            {
                // Success
                oRC = 0;
            }
        }
    }

    return oRC;
}

int Style::ToXMLNode(QDomNode &iParentNode) const
{
    int oRC = 1;

    // First, create valid element node
    QDomDocument doc = iParentNode.ownerDocument();
    QDomElement Node = doc.createElement(STYLE);
    if (!Node.isNull())
    {
        if (!_Name.isEmpty() && _Version>0 &&
            _OGMin<=_OGMax &&
            _FGMin <= _FGMax &&
            _OGMin >= _FGMin &&
            _OGMax >= _FGMax &&
            _IBUMin <= _IBUMax &&
            _ColorMin.Value() <= _ColorMax.Value())
        {
            // Name
            QDomElement Elem = doc.createElement(STYLE_NAME);
            Q_ASSERT(!Elem.isNull());
            QDomText Text = doc.createTextNode(_Name);
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);

            // Version
            Elem = doc.createElement(STYLE_VERSION);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_Version));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Category
            Elem = doc.createElement(STYLE_CATEGORY);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(_Category);
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Category number
            Elem = doc.createElement(STYLE_CATNUM);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_CategoryNum));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style letter
            Elem = doc.createElement(STYLE_GUIDE);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(_StyleLetter);
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style letter
            Elem = doc.createElement(STYLE_LETTER);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(_StyleGuide);
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style Type
            QString StyleType;
            switch (_Style)
            {
            case STLager:
                StyleType = STYLE_TYPE_LAGER;
                break;
            case STAle:
                StyleType = STYLE_TYPE_ALE;
                break;
            case STWheat:
                StyleType = STYLE_TYPE_WHEAT;
                break;
            case STMead:
                StyleType = STYLE_TYPE_MEAD;
                break;
            case STMixed:
                StyleType = STYLE_TYPE_MIXED;
                break;
            case STCider:
                StyleType = STYLE_TYPE_CIDER;
                break;
            }
            Elem = doc.createElement(STYLE_GUIDE);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(StyleType);
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style OGMin
            Elem = doc.createElement(STYLE_OGMIN);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_OGMin));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style OGMax
            Elem = doc.createElement(STYLE_OGMAX);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_OGMax));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style FGMin
            Elem = doc.createElement(STYLE_FGMIN);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_FGMin));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style FGMax
            Elem = doc.createElement(STYLE_FGMAX);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_FGMax));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);

            // Style IBUMin
            Elem = doc.createElement(STYLE_IBUMIN);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_IBUMin));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style IBUMax
            Elem = doc.createElement(STYLE_IBUMAX);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_IBUMax));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);

            // Style ColorMin
            Elem = doc.createElement(STYLE_COLORMIN);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_ColorMin.Value(SRM)));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // Style ColorMax
            Elem = doc.createElement(STYLE_COLORMAX);
            Q_ASSERT(!Elem.isNull());
            Text = doc.createTextNode(QString::number(_ColorMax.Value(SRM)));
            Q_ASSERT(!Text.isNull());
            Elem.appendChild(Text);
            Node.appendChild(Elem);
            
            // ABV
            if (_ABVMin<=_ABVMax && 0<=_ABVMin)
            {
                // Style ABVMin
                Elem = doc.createElement(STYLE_ABVMIN);
                Q_ASSERT(!Elem.isNull());
                Text = doc.createTextNode(QString::number(_ABVMin));
                Q_ASSERT(!Text.isNull());
                Elem.appendChild(Text);
                Node.appendChild(Elem);
            
                // Style ABVMax
                Elem = doc.createElement(STYLE_ABVMAX);
                Q_ASSERT(!Elem.isNull());
                Text = doc.createTextNode(QString::number(_ABVMax));
                Q_ASSERT(!Text.isNull());
                Elem.appendChild(Text);
                Node.appendChild(Elem);                
            }
            
            // Notes
            if (!_Notes.isEmpty())
            {            
                Elem = doc.createElement(STYLE_EXAMPLES);
                Q_ASSERT(!Elem.isNull());
                Text = doc.createTextNode(_Notes);
                Q_ASSERT(!Text.isNull());
                Elem.appendChild(Text);
                Node.appendChild(Elem);                
            }
            
            // Examples
            if (!_Examples.isEmpty())
            {            
                Elem = doc.createElement(STYLE_NOTES);
                Q_ASSERT(!Elem.isNull());
                Text = doc.createTextNode(_Examples);
                Q_ASSERT(!Text.isNull());
                Elem.appendChild(Text);
                Node.appendChild(Elem);                
            }

            // Update output
            QDomNode zeNode = iParentNode.appendChild(Node);
            if (!zeNode.isNull())
                oRC = 0;
        }
    }

    return oRC;
}
